#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <utime.h>
#include <sys/time.h>
#include <time.h>
#include <stdlib.h>

void Change_P(char *src, char *dest)//修改当前文件夹路径
{
    strcat(src, (char *)"/");
    strcat(src, dest);
}

void Change_A(const char *src, const char *dst)//复制源文件属性并修改目标文件
{
    struct stat attr_of_src;
    lstat(src, &attr_of_src);
    chmod(dst, attr_of_src.st_mode);
    chown(dst, attr_of_src.st_uid, attr_of_src.st_gid);
    struct timeval time_buf[2];
    time_buf[1].tv_sec = attr_of_src.st_mtime;
    time_buf[0].tv_sec = attr_of_src.st_atime;
    struct utimbuf tbuf;
    tbuf.actime = attr_of_src.st_atime;
    tbuf.modtime = attr_of_src.st_mtime;
    utime(dst, &tbuf);
    struct stat dst_attr_of_src;
    lstat(dst, &dst_attr_of_src);
}

void copy_file(const char *src_file, const char *dest_file)//标准文件复制
{
    int src_file_descriptor = open(src_file, O_RDONLY);
    int dest_file_descriptor = creat(dest_file, O_WRONLY);
    unsigned char BUFFER[500];
    while (read(src_file_descriptor, BUFFER, sizeof(BUFFER)) > 0)//将源文件内容先存入缓冲区并写入目标文件中
        write(dest_file_descriptor, BUFFER, sizeof(BUFFER));
    Change_A(src_file, dest_file);
    close(src_file_descriptor);
    close(dest_file_descriptor);
}

void copy_dir(const char *src_dir, const char *dest_dir)//建立文件目录文件
{
    mkdir(dest_dir, 0777);//以可读可写可执行建立新文件目录
    Change_A(src_dir, dest_dir);//更改新文件目录属性
}

void copy_softlink(const char *src_file, const char *dest_file)//复制软链接文件
{
    char BUFFER[500];
    int length = readlink(src_file, BUFFER, sizeof(BUFFER));
    if (length > 0)
    {
        if (symlink(BUFFER, dest_file) == -1)
            perror("symlink");
    }
    Change_A(src_file, dest_file);
}

void dfs(char *src_path, char *dest_path)//深度优先搜索遍历文件夹中内容
{
    DIR *src_dir = opendir(src_path);
    DIR *dest_dir = opendir(dest_path);
    struct dirent *entry = NULL;
    struct stat state_tar;
    while ((entry = readdir(src_dir)) != NULL)
    {
        lstat(entry->d_name, &state_tar);
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
            continue;
        if (S_ISLNK(state_tar.st_mode))//当是软连接文件时
        {
            char src_file[500];
            char dest_file[500];
            strcpy(src_file, src_path);
            Change_P(src_file, entry->d_name);
            strcpy(dest_file, dest_path);
            Change_P(dest_file, entry->d_name);
            copy_softlink(src_file, dest_file);
        }
        else if (S_ISREG(state_tar.st_mode))//当是标准文件时
        {
            char src_file[500];
            char dest_file[500];
            strcpy(src_file, src_path);
            Change_P(src_file, entry->d_name);
            strcpy(dest_file, dest_path);
            Change_P(dest_file, entry->d_name);
            copy_file(src_file, dest_file);
        }
        else if (S_ISDIR(state_tar.st_mode)) //当是目录时
        {
            char src[500];
            char dest[500];
            strcpy(src, src_path);
            Change_P(src, entry->d_name);
            strcpy(dest, dest_path);
            Change_P(dest, entry->d_name);
            copy_dir(src, dest);
            dfs(src, dest);
        }
    }
}

int main(int argc, char const *argv[])//主函数
{
    if (!access(argv[2], F_OK))//如果没有目标文件夹则建立目标文件夹
        mkdir(argv[2], 0777);
    char src[100], dest[100];
    char *cur_dir = getcwd(NULL, 0);
    struct stat state_tar;
    if (argc != 3)//参数不足
    {
        printf("请输入正确参数！");
        return 0;
    }
    lstat(argv[1], &state_tar);
    if (S_ISDIR(state_tar.st_mode)) //当时文件夹时
    {
        if (chdir(argv[1]))
        {
            perror("chdir");
            return 0;
        }
        strcpy(src, getcwd(NULL, 0));
        chdir(cur_dir);
        lstat(argv[2], &state_tar);
        if (S_ISDIR(state_tar.st_mode))
        {
            if (chdir(argv[2]))
            {
                perror("chdir");
                return 0;
            }
            strcpy(dest, getcwd(NULL, 0));
            chdir(cur_dir);
            chdir(dest);
            chdir(src);
            dfs(src, dest);
        }
    }

    else //当时文件时
    {
        char dest[100];
        lstat(argv[2], &state_tar);
        if (S_ISDIR(state_tar.st_mode))
            strcpy(dest, getcwd(NULL, 0));
        else
        {
            strcpy(dest, "./");
            strcat(dest, argv[2]);
        }
        copy_file(argv[1], argv[2]);
    }

    return 0;
}
